package pseudoAssemblerCompiler.twopassassembler.controller;

import org.apache.commons.lang.StringUtils;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Display;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.AvailableAssemblers;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.FirstAssembler;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.IViewModel;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.SecondAssembler;
import pseudoAssemblerCompiler.twopassassembler.model.assemblerAlgorithms.ThirdAssembler;
import pseudoAssemblerCompiler.twopassassembler.model.entities.OperationCode;
import pseudoAssemblerCompiler.twopassassembler.model.io.IReader;
import pseudoAssemblerCompiler.twopassassembler.model.io.ProgramReaderParser;
import pseudoAssemblerCompiler.twopassassembler.model.io.ReaderImpl;
import pseudoAssemblerCompiler.twopassassembler.view.windows.AddCodeOperationWindow;
import pseudoAssemblerCompiler.twopassassembler.view.windows.LoadProgramFileWindow;
import pseudoAssemblerCompiler.twopassassembler.view.windows.NewProgramFileWindow;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 * User: Администратор
 * Date: 15.04.12
 * Time: 19:46
 * To change this template use File | Settings | File Templates.
 */
public class OperationCodeControllerImpl extends AbstractController
        implements OperationCodeController, IController {

    private static final Logger LOGGER =
            LoggerFactory.getLogger(OperationCodeControllerImpl.class);

    public OperationCodeControllerImpl() {
        super();
    }

    @Override
    public Map<String, OperationCode> getCodeOperationTableElements() {
        return getCodeOperationTable().getCodeOperationTableElements();
    }

    @Override
    public void addOperationCode() {
        //
    }

    @Override
    public void saveOperationCode(OperationCode code) {
        getCodeOperationTable().addCodeOperation(code.get_mnemonicCode(), code);
    }

    @Override
    public void removeOperationCode(OperationCode code) {
        getCodeOperationTable().removeCodeOperation(code.get_mnemonicCode());
    }

    @Override
    public void loadCodeOperationTable() {
        List<OperationCode> operationCodes =
                new ArrayList<OperationCode>(codeOperationTableModel.getCodeOperationTableElements().values());
        codeOperationTableView.setInput(operationCodes);
        LOGGER.debug("invoke loadCodeOperationTable() with {} operationCodes", operationCodes.size());
        _isCodeOperationTableLoaded = true;
        enablePassButtons(true, false);
    }

    private boolean _isCodeOperationTableLoaded;
    private boolean _isFileOpen;

    private void enablePassButtons(boolean enableFirstPass, boolean enableSecondPass) {
        if(_isCodeOperationTableLoaded && _isFileOpen) {
            mainView.enableFirstPassButton(enableFirstPass);
            mainView.enableSecondPassButton(enableSecondPass);
        }
    }

    @Override
    public void loadReferenceTable() {
        List<String[]> referenceTableElements = referenceTableModel.getReferenceTableElements();
        referenceTableView.setInput(referenceTableElements);
        LOGGER.debug("invoke loadCodeOperationTable() with {} operationCodes", referenceTableElements.size());
    }

    @Override
    public void loadSymbolicNamesTable() {
        Map<String, String> symbolicNamesMap = symbolicNamesTableModel.getSymbolNames();
        List<String[]> symbolicNamesTableElements = new ArrayList<String[]>();
        Set<Map.Entry<String, String>> map = symbolicNamesMap.entrySet();
        for (Map.Entry<String, String> symbolicName : map) {
            symbolicNamesTableElements.add(
                    new String[]{symbolicName.getKey(),
                                 symbolicName.getValue()}
            );
        }
        symbolicNamesTableView.setInput(symbolicNamesTableElements);
        LOGGER.debug("invoke loadCodeOperationTable() with {} operationCodes", symbolicNamesTableElements.size());
    }

    @Override
    public void openProgram(File selectedFile) {
        if (selectedFile == null) {
            throw new IllegalArgumentException("Файл не выбран");
        }
        if (StringUtils.isEmpty(selectedFile.getName())) {
            throw new IllegalArgumentException("Файл не может существовать без названия");
        }

        IReader reader = new ReaderImpl();
        StyledText newProgramText = editor.openNewEditTab(selectedFile.getName());
        ProgramReaderParser parser = new ProgramReaderParser(newProgramText);
        try {
            reader.readFile(selectedFile, parser);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void newProgram() {
        NewProgramFileWindow window =
                new NewProgramFileWindow(Display.getCurrent(), "Создание новой программы");
        window.setController(this);
        window.create();
        _isFileOpen = true;
        enablePassButtons(true, false);
    }

    @Override
    public void openProgram() {
        LoadProgramFileWindow window =
                new LoadProgramFileWindow(Display.getCurrent(), "Выбор программы для открытия");
        window.setController(this);
        window.create();
        _isFileOpen = true;
        enablePassButtons(true, false);
    }

    @Override
    public void firstPassButtonPressed() {
        try {
            mainModel.firstPass(editor.getText());
            loadSymbolicNamesTable();
            loadReferenceTable();
            if(!mainModel.hasFirstPassErrors()) {
                enablePassButtons(false, true);
            }
        } catch (Exception ex) {
            console.error(ex.getMessage());
            console.getTextField().append(System.getProperty("line.separator"));
        }
    }

    @Override
    public void error(String error) {
        console.error(error);
        console.getTextField().append(System.getProperty("line.separator"));
        System.out.println(editor.getText().length() + " " +
        mainModel.getStart() + " " + mainModel.getLength());
        editor.showError(mainModel.getStart(), mainModel.getLength());
    }

    private void info(String info) {
        console.getTextField().append(info);
        console.getTextField().append(System.getProperty("line.separator"));
    }

    @Override
    public void addCodeOperation() {
        AddCodeOperationWindow window =
                new AddCodeOperationWindow(Display.getCurrent(), "Добавление кода операции");
        window.setController(this);
        window.create();
    }

    @Override
    public void secondPassButtonPressed() {
        mainModel.secondPass();
        enablePassButtons(true, false);
    }

    @Override
    public void newProgram(String programName) {
        editor.openNewEditTab(programName);
    }

    @Override
    public void setAssemblerAlgorithm(AvailableAssemblers algorithm) {
        switch (algorithm) {
            case FIRST_ASSEMBLER:
                this.addModel(new FirstAssembler(codeOperationTableModel, symbolicNamesTableModel, referenceTableModel));
                break;
            case SECOND_ASSEMBLER:
                this.addModel(new SecondAssembler(codeOperationTableModel, symbolicNamesTableModel, referenceTableModel));
                break;
            case THIRD_ASSEMBLER:
                this.addModel(new ThirdAssembler(codeOperationTableModel, symbolicNamesTableModel, referenceTableModel));
                break;
        }
    }

    @Override
    public void clearConsole() {
        console.getTextField().setText("");
    }

    @Override
    public void firstPass(String aProgramm) {
        mainModel.firstPass(aProgramm);
    }

    @Override
    public void secondPass() {
//        Window addReglament = new AddCodeOperationWindow(Display.getCurrent(),
//                "Добавления условия в регламент опроса", null);
//        addReglament.setController(this);
//        addReglament.setAttribute(_selectedAttribute);
//        addReglament.create();
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void update(IViewModel model, Constants constant) {
//        if (!registeredModels.contains(model)) {
//            return;
//        }
        switch ((Constants.OperationCodeController) constant) {
            case ERROR:
                error(mainModel.getErrorBuffer());
                break;
            case INFO:
                info(mainModel.getInfoBuffer());
                break;
        }
    }

}
